package com.aispeech.detector;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import com.aispeech.detector.utils.FileUtils;
import com.aispeech.detector.utils.VoiceRecorder;
import com.aispeech.detector.view.helpPopup.RecordHelpPopupWindow;
import com.aispeech.detector.view.hintSpinner.HintSpinner;
import com.aispeech.detector.view.voiceView.MicAmplitudeView;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.widget.PopupWindowCompat;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.AppSettingsDialog;
import pub.devrel.easypermissions.EasyPermissions;

public class AudioRecordActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener, View.OnClickListener, EasyPermissions.PermissionCallbacks,
        EasyPermissions.RationaleCallbacks {
    private static final String TAG = "AudioRecordActivity";

    private static final long UPDATE_WAVE_VIEW_PERIOD = 40;//1s

    private HintSpinner mHSpinPhyChannels, mHSpinRecordMicSource, mHSpinRecordSampleRate,
            mHSpinRecordChannels, mHSpinPlaySelectSourceType, mHSpinPlaySelectAudioChannel, mHSpinPlaySelectAudio;
    private MicAmplitudeView mMicAmpView;
    private Button mBtnRecord, mBtnPlay;
    private Button mBtnDeleteHalAudio, mBtnDeleteAppAudio;
    private TextView mTvHalInfo, mTvHalAudioSavePath, mTvRecordSavePath;
    private Switch mSwHalDebug;
    private Chronometer mRecChronometer;
    private RecordHelpPopupWindow mHelpPopWindow;

    private static final int MSG_START_RECORD = 1;
    private static final int MSG_STOP_RECORD = 2;
    private static final int MSG_UPDATE_WAVE_VIEW = 10;

    private Handler mHandler;
    private AudioManager mAudioManager;
    private VoiceRecorder mVoiceRecorder;

    private static final String KEY_HAL_VERSION = "hal_version";
    private static final String KEY_DECODE_VERSION = "aispeech_decode_version";
    private static final String KEY_PCM_SAVE_ENABLE = "pcm_save_enable";
    private static final String KEY_PCM_SAVE_LOCATION = "pcm_save_location";

    private static final String LOCATION_SDCARD_PATH = "/sdcard/aispeech/";
    private static final String LOCATION_INTERNAL_PATH = "/data/misc/media/";
    private static final String LOCATION_INTERNAL_PATH2 = "/data/misc/audioserver/";

    private boolean mIsRecording = false;

    private static final String[] RECORD_PERMISSIONS = {
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
    };
    private static final int RC_RECORD_PERM = 123;

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case android.R.id.home:
                onBackPressed();
                break;
            case R.id.menu_hal_help:
                PopupWindowCompat.showAsDropDown(mHelpPopWindow, findViewById(R.id.tv_hal_debug_title), 0, 0, Gravity.START);
            case R.id.menu_record_help:
                PopupWindowCompat.showAsDropDown(mHelpPopWindow, findViewById(R.id.tv_record_param_title), 0, 0, Gravity.START);
            case R.id.menu_play_help:
                PopupWindowCompat.showAsDropDown(mHelpPopWindow, findViewById(R.id.tv_play_title), 0, 0, Gravity.START);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.recorder, menu);
        return true;
    }

    private void setActionBar() {
        ActionBar actionBar = getSupportActionBar();
        actionBar.setHomeButtonEnabled(true);
        actionBar.setDisplayHomeAsUpEnabled(true);
        setTitle(R.string.actionbar_title_audio_record);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
        setContentView(R.layout.activity_audio_record);
        mHelpPopWindow = new RecordHelpPopupWindow(this);
        setActionBar();
        permissionTask();
    }

    private void permissionOK() {
        Log.d(TAG, "permissionOK: ");
        createHandler();
        initView();
        initRecord();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");

    }

    private String getRecordInfo() {
        String info;
        int channels;

        info = mVoiceRecorder.getAudioSource() + "src";
        info = info + mVoiceRecorder.getSampleRate() + "hz";
        switch (mVoiceRecorder.getChannelConfig()) {
            case AudioFormat.CHANNEL_IN_MONO:
                channels = 1;
                break;
            case AudioFormat.CHANNEL_IN_STEREO:
                channels = 2;
                break;
            case 0x3fc:
                channels = 8;
                break;
            default:
                channels = 0;
                break;
        }
        info = info + channels + "ch";

        return info;
    }

    private void initRecord() {
        Log.d(TAG, "initRecord: ");
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mVoiceRecorder = new VoiceRecorder(mAudioManager);
        String halVersion = mAudioManager.getParameters(KEY_HAL_VERSION);
        String decodeVersion = mAudioManager.getParameters(KEY_DECODE_VERSION);
        Log.d(TAG, "halVersion is " + halVersion + ", decodeVersion is " + decodeVersion);

        if ((halVersion != null) && (halVersion.startsWith(KEY_HAL_VERSION))) {
            String halDebugInfo = halVersion.substring(KEY_HAL_VERSION.length() + 1);
            if ((decodeVersion != null) && (decodeVersion.startsWith(KEY_DECODE_VERSION))) {
                halDebugInfo = halDebugInfo + " (" + decodeVersion.substring(KEY_DECODE_VERSION.length() + 1) + ")";
            }
            mTvHalInfo.setText(halDebugInfo);
        }
    }

    private boolean startRecord() {
        Log.d(TAG, "startRecord: ");
        if (null != mVoiceRecorder && !mVoiceRecorder.isRecording()) {
            String filePath = FileUtils.sdcardPcmFilePath(this, getRecordInfo());
            Log.d(TAG, "startRecord: " + filePath);
            mTvRecordSavePath.setText(filePath);
            mBtnRecord.setText(R.string.btn_record_stop);
            mBtnRecord.setBackgroundColor(getResources().getColor(R.color.recording));
            mVoiceRecorder.setOutputFile(filePath);
            mVoiceRecorder.start();

            mRecChronometer.setBase(SystemClock.elapsedRealtime());
            mRecChronometer.start();
            return true;
        }
        return false;
    }

    private boolean stopRecord() {
        Log.d(TAG, "stopRecord: ");
        if (null != mVoiceRecorder && mVoiceRecorder.isRecording()) {
            mVoiceRecorder.stop();
            mBtnRecord.setText(R.string.btn_record_start);
            mBtnRecord.setBackgroundColor(getResources().getColor(R.color.colorWhite));
            mRecChronometer.stop();
            mMicAmpView.setAlive(false);

        }
        return false;
    }


    private void createHandler() {
        mHandler = new Handler(getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_START_RECORD:
                        mIsRecording = startRecord();
                        if (mIsRecording) {
                            mHandler.sendEmptyMessageDelayed(MSG_UPDATE_WAVE_VIEW, UPDATE_WAVE_VIEW_PERIOD);
                        }
                        break;
                    case MSG_STOP_RECORD:
                        mIsRecording = stopRecord();
                        break;
                    case MSG_UPDATE_WAVE_VIEW:
                        updateWaveView();
                        break;
                }
            }
        };
    }

    private void updateWaveView() {
        if (null != mVoiceRecorder && mVoiceRecorder.isRecording()) {
//            int phyChannels = mVoiceRecorder.getPhyChannels();
            short[] channelsAmplitudeArray = mVoiceRecorder.getChannelsAmplitudeArray();
            mMicAmpView.setAlive(true);

            for (int i = 0; i < channelsAmplitudeArray.length; ++i) {
                mMicAmpView.setChannelsAmp(i, channelsAmplitudeArray[i]);
            }

            if (mIsRecording) {
                mHandler.sendEmptyMessageDelayed(MSG_UPDATE_WAVE_VIEW, UPDATE_WAVE_VIEW_PERIOD);
            }
        }
    }

    private void initView() {
        initRecordSpinner();
        initMicWav();
        mBtnRecord = findViewById(R.id.btn_ctrl_record);
        mBtnPlay = findViewById(R.id.btn_ctrl_play);
        mBtnDeleteHalAudio = findViewById(R.id.btn_delete_hal_files);
        mBtnDeleteAppAudio = findViewById(R.id.btn_delete_record_files);
        mTvHalInfo = findViewById(R.id.tv_hal_debug_info);
        mTvHalAudioSavePath = findViewById(R.id.tv_hal_audio_save_path);
        mTvRecordSavePath = findViewById(R.id.tv_record_save_path);
        mSwHalDebug = findViewById(R.id.sw_hal_debug);
        mRecChronometer = findViewById(R.id.cr_record_time);
        mRecChronometer.setFormat(getResources().getString(R.string.record_chronometer_format));

        mSwHalDebug.setOnCheckedChangeListener(this);
        mBtnRecord.setOnClickListener(this);
    }

    private void initRecordSpinner() {
        mHSpinPhyChannels = findViewById(R.id.hsp_phy_channels);
        mHSpinPhyChannels.setHintTextColor(Color.GRAY);
//        mHSpinPhyChannels.setSelectedItemHintColor(Color.BLUE);
//        mHSpinPhyChannels.setDownArrowTintColor(Color.RED);

        mHSpinPhyChannels.setAdapter(ArrayAdapter.createFromResource(this, R.array.physical_channels, android.R.layout.simple_spinner_item), 0);
        mHSpinPhyChannels.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
                Log.d(TAG, "onItemSelected() called with: position = [" + position + "], itemAtPosition = [" + itemAtPosition + "]");
                mMicAmpView.setChannels(position + 1);
                mVoiceRecorder.setPhyChannels(position + 1);
            }
        });

        mHSpinRecordMicSource = findViewById(R.id.hsp_param_source);
        mHSpinRecordMicSource.setHintTextColor(Color.GRAY);
        mHSpinRecordMicSource.setAdapter(ArrayAdapter.createFromResource(this, R.array.audio_source, android.R.layout.simple_spinner_item), 0);
        mHSpinRecordMicSource.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
//                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinRecordMicSource.isSelected(), Toast.LENGTH_SHORT).show();
                switch (position) {
                    case 0:
                        mVoiceRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);
                        break;
                    case 1:
                        mVoiceRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        break;
                    case 2:
                        mVoiceRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
                        break;
                    case 3:
                        mVoiceRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
                        break;
                    case 4:
                        mVoiceRecorder.setAudioSource(1997);
                        break;
                    default:
                        break;
                }
            }
        });

        mHSpinRecordSampleRate = findViewById(R.id.hsp_param_sample_rate);
        mHSpinRecordSampleRate.setHintTextColor(Color.GRAY);
        mHSpinRecordSampleRate.setAdapter(ArrayAdapter.createFromResource(this, R.array.audio_sample_rate, android.R.layout.simple_spinner_item), 0);
        mHSpinRecordSampleRate.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
//                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinRecordSampleRate.isSelected(), Toast.LENGTH_SHORT).show();
                switch (position) {
                    case 0:
                        mVoiceRecorder.setSampleRate(16000);
                        break;
                    case 1:
                        mVoiceRecorder.setSampleRate(32000);
                        break;
                    case 2:
                        mVoiceRecorder.setSampleRate(44100);
                        break;
                    case 3:
                        mVoiceRecorder.setSampleRate(48000);
                        break;
                    case 4:
                        mVoiceRecorder.setSampleRate(64000);
                        break;
                    case 5:
                        mVoiceRecorder.setSampleRate(96000);
                        break;
                    case 6:
                        mVoiceRecorder.setSampleRate(128000);
                        break;
                    case 7:
                        mVoiceRecorder.setSampleRate(8000);
                        break;
                    default:
                        break;
                }

            }
        });

        mHSpinRecordChannels = findViewById(R.id.hsp_param_channel);
        mHSpinRecordChannels.setHintTextColor(Color.GRAY);
        mHSpinRecordChannels.setAdapter(ArrayAdapter.createFromResource(this, R.array.audio_channel, android.R.layout.simple_spinner_item), 0);
        mHSpinRecordChannels.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
//                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinRecordChannels.isSelected(), Toast.LENGTH_SHORT).show();
                switch (position) {
                    case 0:
                        mVoiceRecorder.setChannelConfig(AudioFormat.CHANNEL_IN_STEREO);
                        break;
                    case 1:
                        mVoiceRecorder.setChannelConfig(AudioFormat.CHANNEL_IN_MONO);
                        break;
                    case 2:
                        mVoiceRecorder.setChannelConfig(0x3fc);
                        break;
                    default:
                        break;
                }
            }
        });

        mHSpinPlaySelectSourceType = findViewById(R.id.hsp_select_play_type);
        mHSpinPlaySelectSourceType.setHintTextColor(Color.GRAY);
        mHSpinPlaySelectSourceType.setAdapter(ArrayAdapter.createFromResource(this, R.array.play_audio_source_type, android.R.layout.simple_spinner_item), 0);
        mHSpinPlaySelectSourceType.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinPlaySelectAudio.isSelected(), Toast.LENGTH_SHORT).show();
            }
        });

        mHSpinPlaySelectAudioChannel = findViewById(R.id.hsp_select_play_channel);
        mHSpinPlaySelectAudioChannel.setHintTextColor(Color.GRAY);
        mHSpinPlaySelectAudioChannel.setAdapter(ArrayAdapter.createFromResource(this, R.array.physical_channels, android.R.layout.simple_spinner_item), 0);
        mHSpinPlaySelectAudioChannel.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinPlaySelectAudio.isSelected(), Toast.LENGTH_SHORT).show();
            }
        });

        mHSpinPlaySelectAudio = findViewById(R.id.hsp_select_play_audio);
        mHSpinPlaySelectAudio.setHintTextColor(Color.GRAY);
        mHSpinPlaySelectAudio.setAdapter(ArrayAdapter.createFromResource(this, R.array.play_audio_file, android.R.layout.simple_spinner_item), 0);
        mHSpinPlaySelectAudio.setOnSpinnerItemClickListener(new HintSpinner.onSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, String itemAtPosition) {
                Toast.makeText(getApplicationContext(), "Position: " + position + " | Item: " + itemAtPosition + " | isSelected:" + mHSpinPlaySelectAudio.isSelected(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void initMicWav() {
        mMicAmpView = findViewById(R.id.mav_mic_amp_view);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_ctrl_record:
                mHandler.sendEmptyMessage(mIsRecording ? MSG_STOP_RECORD : MSG_START_RECORD);
                break;
            case R.id.btn_ctrl_play:

                break;
            default:

                break;
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked) {
            mAudioManager.setParameters(KEY_PCM_SAVE_ENABLE + "=true");
        } else {
            mAudioManager.setParameters(KEY_PCM_SAVE_ENABLE + "=false");
        }
        updateSwitchState();
    }


    private void updateSwitchState() {
        switch (getPcmSaveLocationIndex()) {
            case 1:
                mSwHalDebug.setChecked(true);
                mTvHalAudioSavePath.setText(LOCATION_SDCARD_PATH);
                break;
            case 2:
                mSwHalDebug.setChecked(true);
                mTvHalAudioSavePath.setText(LOCATION_INTERNAL_PATH);
                break;
            case 3:
                mSwHalDebug.setChecked(true);
                mTvHalAudioSavePath.setText(LOCATION_INTERNAL_PATH2);
                break;
            case 0:
            default:
                mSwHalDebug.setChecked(false);
                mTvHalAudioSavePath.setText(R.string.hal_err_hint);
                break;
        }
    }

    private int getPcmSaveLocationIndex() {
        String valuePcmSaveEnable = mAudioManager.getParameters(KEY_PCM_SAVE_ENABLE);
        String valuePcmSaveEnableValue = "false";
        int mPcmLocationIndex = 0;

        Log.d(TAG, "valuePcmSaveEnable is " + valuePcmSaveEnable);
        if (valuePcmSaveEnable.startsWith(KEY_PCM_SAVE_ENABLE)) {
            valuePcmSaveEnableValue = valuePcmSaveEnable.substring(KEY_PCM_SAVE_ENABLE.length() + 1);
            if (valuePcmSaveEnableValue.startsWith(KEY_PCM_SAVE_ENABLE)) {
                // some platform, such as HiSilicon, need to remove the prefix twice
                valuePcmSaveEnableValue = valuePcmSaveEnableValue.substring(KEY_PCM_SAVE_ENABLE.length() + 1);
            }
        }
        Log.d(TAG, "valuePcmSaveEnableValue is " + valuePcmSaveEnableValue);

        if ("true".equals(valuePcmSaveEnableValue)) {
            String location = mAudioManager.getParameters(KEY_PCM_SAVE_LOCATION);
            String locationValue = "";

            Log.d(TAG, "location is " + location);
            if (location.startsWith(KEY_PCM_SAVE_LOCATION)) {
                locationValue = location.substring(KEY_PCM_SAVE_LOCATION.length() + 1);
                if (locationValue.startsWith(KEY_PCM_SAVE_LOCATION)) {
                    // some platform, such as HiSilicon, need to remove the prefix twice
                    locationValue = locationValue.substring(KEY_PCM_SAVE_LOCATION.length() + 1);
                }
            }

            if (LOCATION_SDCARD_PATH.equals(locationValue)) {
                mPcmLocationIndex = 1;
            } else if (LOCATION_INTERNAL_PATH.equals(locationValue)) {
                mPcmLocationIndex = 2;
            } else if (LOCATION_INTERNAL_PATH2.equals(locationValue)) {
                mPcmLocationIndex = 3;
            } else {
                mPcmLocationIndex = -1;
            }

            Log.d(TAG, "locationValue is " + locationValue);
        }

        return mPcmLocationIndex;
    }

    private void ShowToastInfo(final String info) {
        Log.d(TAG, "ShowToastInfo() called with: info = [" + info + "]");
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(AudioRecordActivity.this, info, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @AfterPermissionGranted(RC_RECORD_PERM)
    public void permissionTask() {
        if (EasyPermissions.hasPermissions(this, RECORD_PERMISSIONS)) {
            permissionOK();
        } else {
            EasyPermissions.requestPermissions(
                    this,
                    getString(R.string.rationale_location_contacts),
                    RC_RECORD_PERM,
                    RECORD_PERMISSIONS);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        Log.d(TAG, "onRequestPermissionsResult() called with: requestCode = [" + requestCode + "]");
        // EasyPermissions handles the request result.
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "onPermissionsGranted() called with: requestCode = [" + requestCode + "], perms = [" + perms.size() + "]");
        permissionOK();
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "onPermissionsDenied() called with: requestCode = [" + requestCode + "], perms = [" + perms.size() + "]");
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            new AppSettingsDialog.Builder(this).build().show();
//            finish();
        } else {
            finish();
        }
    }

    @Override
    public void onRationaleAccepted(int requestCode) {
        Log.d(TAG, "onRationaleAccepted() called with: requestCode = [" + requestCode + "]");
    }

    @Override
    public void onRationaleDenied(int requestCode) {
        Log.d(TAG, "onRationaleDenied() called with: requestCode = [" + requestCode + "]");
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) {
            // Do something after user returned from app settings screen, like showing a Toast.
//            Toast.makeText(
//                    this,
//                    getString(R.string.returned_from_app_settings_to_activity),
//                    Toast.LENGTH_LONG)
//                    .show();
            if (!EasyPermissions.hasPermissions(this, RECORD_PERMISSIONS)) {
                finish();
            }
        }
    }
}
